﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
#if(NET35)
using LightFx.Linq.Expressions;
#endif
using System.Data.Common;

namespace Orm.LinqParse
{
    /// <summary>
    /// 询问刘哥该类
    /// </summary>
    internal class WhereTranslator : ExpressionVisitor
    {
        private Func<string, string> _sqlText;
        private Func<object, string> _dbParameter;
        private string _startsWithText = string.Empty;
        private string _endsWithText = string.Empty;

        internal WhereTranslator() { }

        public void Translate(Expression expression, Func<string, string> sqlText, Func<object, string> dbParameter)
        {
            this._sqlText = sqlText;
            this._dbParameter = dbParameter;
            this.Visit(Evaluator.PartialEval(expression));
        }


        private static Expression StripQuotes(Expression e)
        {
            while (e.NodeType == ExpressionType.Quote)
            {
                e = ((UnaryExpression)e).Operand;
            }
            return e;
        }


        protected override Expression VisitMethodCall(MethodCallExpression m)
        {
            switch (m.Method.Name)
            {
                case "StartsWith":
                    this.Visit(m.Object);
                    _sqlText(" LIKE ");
                    _startsWithText = "%";
                    this.Visit(m.Arguments[0]);
                    _startsWithText = string.Empty;
                    return m;
                case "EndsWith":
                    this.Visit(m.Object);
                    _sqlText(" LIKE ");
                    _endsWithText = "%";
                    this.Visit(m.Arguments[0]);
                    _endsWithText = string.Empty;
                    return m;
                case "Contains":
                    this.Visit(m.Object);
                    _sqlText(" LIKE ");
                    _startsWithText = "%";
                    _endsWithText = "%";
                    this.Visit(m.Arguments[0]);
                    _startsWithText = string.Empty;
                    _endsWithText = string.Empty;
                    return m;
            }
            throw new NotSupportedException(string.Format("The method '{0}' is not supported", m.Method.Name));
        }


        protected override Expression VisitUnary(UnaryExpression u)
        {
            switch (u.NodeType)
            {
                case ExpressionType.Not:
                    _sqlText(" NOT ");
                    this.Visit(u.Operand);
                    break;
                case ExpressionType.Convert:
                    this.Visit(u.Operand);
                    break;
                default:
                    throw new NotSupportedException(string.Format("The unary operator '{0}' is not supported", u.NodeType));
            }
            return u;
        }


        protected override Expression VisitBinary(BinaryExpression b)
        {
            _sqlText("(");
            this.Visit(b.Left);
            switch (b.NodeType)
            {
                case ExpressionType.And:
                case ExpressionType.AndAlso:
                    _sqlText(" AND ");
                    break;
                case ExpressionType.Or:
                case ExpressionType.OrElse:
                    _sqlText(" OR ");
                    break;
                case ExpressionType.Equal:
                    _sqlText(" = ");
                    break;
                case ExpressionType.NotEqual:
                    _sqlText(" <> ");
                    break;
                case ExpressionType.LessThan:
                    _sqlText(" < ");
                    break;
                case ExpressionType.LessThanOrEqual:
                    _sqlText(" <= ");
                    break;
                case ExpressionType.GreaterThan:
                    _sqlText(" > ");
                    break;
                case ExpressionType.GreaterThanOrEqual:
                    _sqlText(" >= ");
                    break;
                default:
                    throw new NotSupportedException(string.Format("The binary operator '{0}' is not supported", b.NodeType));
            }
            this.Visit(b.Right);
            _sqlText(")");
            return b;
        }


        protected override Expression VisitConstant(ConstantExpression c)
        {
            IQueryable q = c.Value as IQueryable;
            if (q != null)
            {
                // assume constant nodes w/ IQueryables are table references
                _sqlText("SELECT * FROM ");
                _sqlText(q.ElementType.Name);
            }
            else if (c.Value == null)
            {
                _sqlText("NULL");
            }
            else
            {
                switch (Type.GetTypeCode(c.Value.GetType()))
                {
                    case TypeCode.Boolean:
                        _sqlText(_dbParameter(((bool)c.Value) ? 1 : 0));
                        break;
                    case TypeCode.String:
                    case TypeCode.Char:
                        _sqlText(_dbParameter(_endsWithText + c.Value + _startsWithText));
                        break;
                    #region 注释
                    //case TypeCode.Object:
                    //    if (c.Value is Guid)
                    //    {
                    //        _sqlText(_dbParameter(c.Value));
                    //    }
                    //    else
                    //    {
                    //        throw new NotSupportedException(string.Format("The constant for '{0}' is not supported", c.Value));
                    //    }
                    //    break;
                    #endregion
                    default:
                        _sqlText(_dbParameter(c.Value));
                        break;
                }
            }
            return c;
        }

#if(NET35)
        protected override Expression VisitMemberAccess(MemberExpression m)
#endif
#if(NET4)
        protected override Expression VisitMember(MemberExpression m)
#endif
        {
            if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter)
            {
                _sqlText("["+m.Member.Name+"]");
                return m;
            }
            throw new NotSupportedException(string.Format("The member '{0}' is not supported", m.Member.Name));
        }
    }
}
